﻿Imports System.Runtime.InteropServices

''' <summary>
''' 包含从Winamp获取信息的方法
''' </summary>
''' <remarks></remarks>
Public Class WinampInfo

    ''' <summary>
    ''' SendMessage函数要用的常量
    ''' </summary>
    ''' <remarks></remarks>
    Const WM_USER = &H400L

    ''' <summary>
    ''' 创建该类的一个实例，将会自动寻找Winamp的主进程和句柄
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()
        RefreshState()
    End Sub
    ' <DllImport("User32.dll", CharSet:=CharSet.Auto, EntryPoint:="SendMessage")>
    ' Shared Function SendMessage(ByVal hwnd As IntPtr,ByVal msg As Integer,ByVal wParam As IntPtr, ByVal lParam As Integer) As IntPtr
    ' End Function
    ' <DllImport("User32.dll", CharSet:=CharSet.Auto, EntryPoint:="FindWindow")>
    ' Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    ' End Function

#Region "监视winamp状态"
    Dim wa_prc As Process
    Dim wa_hwnd As IntPtr
    Dim FileLock As Object = New Object
    ''' <summary>
    ''' 获取Winamp的句柄
    ''' </summary>
    ''' <value>Winamp的句柄</value>
    ''' <returns>返回Winamp的句柄</returns>
    ''' <remarks></remarks>
    Private ReadOnly Property WinampHWND As IntPtr
        Get
            If wa_prc Is Nothing OrElse wa_prc.HasExited Then
                RefreshState()
            End If
            Return wa_hwnd
        End Get
    End Property

    ''' <summary>
    ''' 获取Winamp的进程
    ''' </summary>
    ''' <value>Winamp的进程</value>
    ''' <returns>返回Winamp的进程</returns>
    ''' <remarks></remarks>
    Private ReadOnly Property WinampProcess As Process
        Get
            If wa_prc Is Nothing OrElse wa_prc.HasExited Then
                RefreshState()
            End If
            Return wa_prc
        End Get
    End Property

    ''' <summary>
    ''' 自动获取Winamp主进程和句柄的方法
    ''' </summary>
    ''' <remarks></remarks>
    Sub RefreshState()
        wa_prc = Nothing
        For Each p In Process.GetProcessesByName("Winamp")
            '进程名大致是"Winamp.exe"，或者就是"Winamp"
            '验证该进程是winamp.exe，如果Winamp是以管理员状态启动可能无法获得命令行
            If IO.Path.GetFileNameWithoutExtension(p.MainModule.FileName).ToLower.Equals("winamp") Then
                wa_prc = p
                Exit For
            End If
        Next
        If wa_prc IsNot Nothing Then
            '找到Winamp进程
            wa_hwnd = FindWindow("Winamp v1.x", Nothing)
            WinampCurrentState = GetPlayStatus()
        Else
            '没有Winamp进程
            WinampCurrentState = WinampState.Closed
            wa_hwnd = IntPtr.Zero
        End If
    End Sub

    ''' <summary>
    ''' 获取或设置Winamp当前状态
    ''' </summary>
    ''' <value>Winamp的状态</value>
    ''' <returns>返回Winamp的状态</returns>
    ''' <remarks></remarks>
    Protected Property WinampCurrentState As WinampState

    ''' <summary>
    ''' 表示Winamp的状态
    ''' </summary>
    ''' <remarks></remarks>
    <Flags()>
    Enum WinampState
        ''' <summary>
        ''' 表示Winamp正在播放音乐
        ''' </summary>
        ''' <remarks></remarks>
        Playing = 0
        ''' <summary>
        ''' 表示Winamp已暂停
        ''' </summary>
        ''' <remarks></remarks>
        Pause = 1
        ''' <summary>
        ''' 表示Winamp已停止
        ''' </summary>
        ''' <remarks></remarks>
        Stopped = 2
        ''' <summary>
        ''' 表示未知的Winamp状态，或无法获取当前状态
        ''' </summary>
        ''' <remarks></remarks>
        Unknown = 8
        ''' <summary>
        ''' 表示Winamp已经或即将关闭
        ''' </summary>
        ''' <remarks></remarks>
        Closed = 4
    End Enum
#End Region

    ''' <summary>
    ''' 通知Winamp将当前播放列表写入Winamp安装目录的Winamp.m3u文件，并返回正在播放文件的序号（从0开始）
    ''' </summary>
    ''' <returns>返回正在播放文件的序号（从0开始）</returns>
    ''' <remarks></remarks>
    Public Function WriteTom3u() As Long
        If GetPlayStatus() = WinampState.Playing Or GetPlayStatus() = WinampState.Pause Then
            Return CLng(SendMessage(WinampHWND, WM_USER, 0L, 120L))
        Else
            Return -1
        End If
    End Function

    ''' <summary>
    ''' 获取Winamp程序的安装目录（根据Winamp进程的命令行来判断）
    ''' </summary>
    ''' <returns>返回Winamp程序的安装目录</returns>
    ''' <remarks></remarks>
    Public Function GetWinampPath() As String
        If WinampCurrentState = WinampState.Closed Then Return String.Empty
        If Not WinampProcess Is Nothing Then
            Return WinampProcess.MainModule.FileName
        End If
        Return String.Empty
    End Function



#Region "文件切换监视"
    Dim r As TagReader

    Private _CurrentFile As String = String.Empty
    ''' <summary>
    ''' 获取当前正在播放文件的路径（从Winamp.m3u读取）
    ''' </summary>
    ''' <value>当前正在播放文件的路径（从Winamp.m3u读取）</value>
    ''' <returns>返回当前正在播放文件的路径（从Winamp.m3u读取）</returns>
    ''' <remarks></remarks>
    Public Property CurrentFile As String
        Get
            GetFilePath()
            Return _CurrentFile
        End Get
        Protected Friend Set(ByVal value As String)
            _CurrentFile = value
        End Set
    End Property

    ''' <summary>
    ''' 获取当前正在播放文件的路径（从Winamp.m3u读取）
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetFilePath() As String
        SyncLock FileLock
            Try
                Dim c As Long = WriteTom3u()
                Dim m3u As String = IO.Path.Combine(IO.Path.GetDirectoryName(GetWinampPath), "Winamp.m3u")
                Dim reader = My.Computer.FileSystem.OpenTextFileReader(m3u, System.Text.Encoding.Default)
                Dim s As String = String.Empty
                For i As Long = 0 To c
                    s = reader.ReadLine
                    If String.IsNullOrEmpty(s) OrElse s(0) = "#"c Then
                        i -= 1L
                    End If
                Next i
                reader.Close()
                If Not IO.File.Exists(s) Then
                    s = IO.Path.Combine(IO.Path.GetPathRoot(GetWinampPath), s)
                    If Not IO.File.Exists(s) Then
                        s = String.Empty
                    End If
                End If
                CurrentFile = s
                Return s
            Catch ex As Exception
                Return _CurrentFile
            End Try
        End SyncLock
    End Function
    Const Fix_time As Long = 100L

    ''' <summary>
    ''' 获取当前播放位置（以毫秒记）
    ''' </summary>
    ''' <returns>返回当前播放位置（以毫秒记）</returns>
    ''' <remarks></remarks>
    Public Function GetTrackPosition() As Long
        Return SendMessage(WinampHWND, WM_USER, 0, 105).ToInt64
    End Function

    ''' <summary>
    ''' 获取当前播放曲目总时长
    ''' </summary>
    ''' <returns>返回当前播放曲目总时长</returns>
    ''' <remarks></remarks>
    Public Function GetTrackTime() As Long
        Return SendMessage(WinampHWND, WM_USER, 2, 105).ToInt64
    End Function

    ''' <summary>
    ''' 获取播放进度百分比
    ''' </summary>
    ''' <returns>返回播放进度百分比</returns>
    ''' <remarks></remarks>
    Public Function ProgressPercents() As String
        Return (CDbl(GetTrackPosition()) / CDbl(GetTrackTime())).ToString("P", Globalization.CultureInfo.CurrentCulture)
    End Function
#End Region

#Region "获取信息方法"
    Public ReadOnly Property CurrentFileTag As TagReader
        Get
            Dim s = CurrentFile
            Try
                r = New TagReader(GetFilePath)
            Catch ex As Exception
                If r IsNot Nothing Then r.Dispose()
                r = Nothing
            End Try
            Return r
        End Get
    End Property

    Public Function GetPlayStatus() As WinampState
        Dim h = WinampHWND
        If h = IntPtr.Zero Then
            WinampCurrentState = WinampState.Closed
            Return WinampState.Closed
        End If
        Select Case CLng(SendMessage(h, WM_USER, 0, 104))
            Case 1L
                WinampCurrentState = WinampState.Playing
                Return WinampState.Playing
            Case 3L
                WinampCurrentState = WinampState.Pause
                Return WinampState.Pause
            Case 0L
                WinampCurrentState = WinampState.Stopped
                Return WinampState.Stopped
            Case Else
                WinampCurrentState = WinampState.Unknown
                Return WinampState.Unknown
        End Select
    End Function

    Public Function GetBitRate() As String
        Dim h = WinampHWND
        If h = IntPtr.Zero Then Return String.Empty
        Return SendMessage(h, WM_USER, 1, 126).ToInt64.ToString
    End Function

    Public Function GetSampleRate() As String
        Dim h = WinampHWND
        If h = IntPtr.Zero Then Return String.Empty
        Return (SendMessage(h, WM_USER, 5, 126).ToInt64 / 1000.0).ToString("G")
    End Function

    Public Function GetChannels() As String
        Dim h = WinampHWND
        If h = IntPtr.Zero Then Return String.Empty
        Return SendMessage(h, WM_USER, 2, 126).ToInt64.ToString
    End Function

    Public Function GetFileType() As String
        Select Case IO.Path.GetExtension(GetFilePath).ToLower
            Case ".wav"
                Return "Wave Form"
            Case ".ogg", ".ogm"
                Return "Vorbis"
            Case ".m4a", ".aac", ".mp4"
                Return "Advanced Audio Coding"
            Case ".mp3"
                Return "MPEG-1 Audio Layer III"
            Case ".wma"
                Return "Windows Media Audio"
            Case ".flac"
                Return "Free Lossless Audio Coding"
            Case ".tta"
                Return "The True Audio"
            Case ".wv"
                Return "Wave Pack"
            Case ".ape", ".mac", ".apl"
                Return "Monkey's Audio"
            Case ".tak"
                Return "Tom's lossless Audio Kompressor"
            Case ".aiff"
                Return "Audio Interchange File Format"
            Case ".mpc", ".mp+", ".mpp"
                Return "Musepack"
            Case ".ac3"
                Return "Dolby Audio Coding-3"
            Case ".dts"
                Return "Digital Theater Systems"
            Case ".mp2"
                Return "MPEG-1 Audio Layer II"
            Case ".mp1"
                Return "MPEG-1 Audio Layer I"
            Case ".amr"
                Return "Adaptive multi-Rate compression"
            Case ".ra"
                Return "RealAudio"
            Case ".cda"
                Return "CD Audio"
            Case ".mka"
                Return "Matroska Audio"
            Case ".as"
                Return "AudioSoft"
            Case ".midi", "mid", "rmi", "kar"
                Return "Musical Instrument Digital Interface"
            Case ".nsa"
                Return "Nullsoft Audio"
            Case ".vqf"
                Return "TwinVQ"
            Case Else
                Return String.Empty
        End Select
    End Function
#End Region
End Class
